﻿using System;
using RayDen.Library.Core.Primitives;

namespace RayDen.Library.Components.Surface
{
    [Serializable]
    public class ArchGlassMaterial : SurfaceBsdf {
        public override BrdfType Type
        {
            get { return BrdfType.Specular | BrdfType.Refractive; }
        }
        public override bool IsRefractive() { return true; }

        public ArchGlassMaterial(RgbSpectrum refl, RgbSpectrum refrct, bool reflSpecularBounce, bool transSpecularBounce) {
            var sp = new RgbSpectrum(0.25f);
            Krefl = RgbSpectrum.Max(ref refl, ref sp);
            Krefrct = RgbSpectrum.Max(ref refrct, ref sp);
            reflectionSpecularBounce = reflSpecularBounce;
            transmitionSpecularBounce = transSpecularBounce;


            float reflFilter = Krefl.Filter();
            transFilter = Krefrct.Filter();
            totFilter = reflFilter + transFilter;

            reflPdf = reflFilter / totFilter;
            transPdf = transFilter / totFilter;
        }

        protected override void EvalParams(ref SurfaceTextureData surfaceData)
        {

            var sp = new RgbSpectrum(0.25f);
            Krefl = RgbSpectrum.Max(ref surfaceData.Specular, ref sp);
            Krefrct = RgbSpectrum.Max(ref surfaceData.Transmittance, ref sp);

            float reflFilter = Krefl.Filter();
            transFilter = Krefrct.Filter();
            totFilter = reflFilter + transFilter;

            reflPdf = reflFilter / totFilter;
            transPdf = transFilter / totFilter;
            
        }
        public override bool IsDiffuse() { return false; }
        public override bool IsSpecular() { return true; }

        public override void f(ref Vector wo, ref Vector wi, ref Normal N, ref RgbSpectrum in_fs, out RgbSpectrum fs) {
            throw new NotSupportedException("Internal error, called GlassMaterial::f()");
        }
        public override float Pdf(ref Vector wo, ref Vector wi, BxDFTypes bxDFType) {
            return 1f;
        }
        public override void Sample_f(ref Vector wo, ref Normal N, ref Normal shadeN, ref RgbSpectrum in_f, float u0, float u1, float u2, ref SurfaceTextureData surfaceData, out BsdfSampleData result) {
            EvalParams(ref surfaceData);

            bool into = (Normal.Dot(ref N, ref shadeN) > 0f);
            result = new BsdfSampleData();
            result.Type = this.Type;

            if (!into) {
                // No internal reflections
                result.Wi = (-wo);
                result.Pdf = 1f;
                result.Type = reflectionSpecularBounce ? BrdfType.Specular : BrdfType.Glossy;
                result.F =  Krefl;
            }
            else {
                // RR to choose if reflect the ray or go trough the glass
                float comp = u0 * totFilter;
                if (comp > transFilter) {
                    Vector mwo = -wo;
                    result.Wi = mwo - (2f * Vector.Dot(ref N, ref mwo)) * N.ToVec();
                    result.Pdf = reflPdf;
                    result.Type = reflectionSpecularBounce ? BrdfType.Specular : BrdfType.Glossy;
                    result.F = Krefrct;
                }
                else {
                    result.Wi = -wo;
                    result.Pdf = transPdf;
                    result.F = Krefrct;
                    result.Type = transmitionSpecularBounce ? BrdfType.Specular : BrdfType.Glossy;
                }
            }

        }
        //public override void Sample_f(ref Vector wo, out Vector wi, ref Normal N, ref Normal shadeN, float u0, float u1, float u2, ref RgbSpectrum col, out BsdfSampleData result)
        //{
        //    RgbSpectrum temp = Krefl;
        //    Krefl = col;
        //    this.Sample_f(ref wo, ref N, ref shadeN, u0, u1, u2, ref hitInfo.TextureData, out result);
        //    Krefl = temp;
        //    wi = result.Wi;
        //}

        public RgbSpectrum GetKrefl() { return Krefl; }
        public RgbSpectrum GetKrefrct() { return Krefrct; }

        private
            RgbSpectrum Krefl, Krefrct;
        float transFilter, totFilter, reflPdf, transPdf;
        public bool reflectionSpecularBounce, transmitionSpecularBounce;
    };
}